from flask import Flask, render_template, request, redirect, url_for, flash, session, Response
from werkzeug.utils import secure_filename
import sqlite3
import os
import oss2
from datetime import datetime
import glob
from bs4 import BeautifulSoup
import hashlib
import time
from dotenv import load_dotenv
from functools import wraps

# 加载 .env 文件
load_dotenv()

app = Flask(__name__)
app.secret_key = 'your_secret_key_here'  # 用于flash消息

# 配置
UPLOAD_FOLDER = 'static/uploads'  # 本地存储路径
HTML_FOLDER = 'templates/content'  # 存放需要被替换图片的HTML文件的文件夹
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}

# 图片访问相关配置
IMAGE_DOMAIN = 'https://simonkuang.tianwen.tech'  # 域名
IMAGE_PATH_PREFIX = '/static/uploads'  # URL 路径前缀

# OSS相关配置从环境变量获取
ALI_OSS_ENDPOINT = os.getenv('ALI_OSS_ENDPOINT')
ALI_APP_KEY = os.getenv('ALI_APP_KEY')
ALI_APP_SECRET = os.getenv('ALI_APP_SECRET')
ALI_OSS_BUCKET = os.getenv('ALI_OSS_BUCKET')
ALI_OSS_KEY_PREFIX = os.getenv('ALI_OSS_KEY_PREFIX')

# 确保上传文件夹存在
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
os.makedirs(HTML_FOLDER, exist_ok=True)

# 添加密码配置
ADMIN_PASSWORD = os.getenv('ADMIN_PASSWORD')

# 登录验证装饰器
def login_required(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if not session.get('logged_in'):
            return redirect(url_for('login'))
        return f(*args, **kwargs)
    return decorated_function

def allowed_file(filename):
    return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

def init_db():
    conn = sqlite3.connect('images.db')
    c = conn.cursor()
    c.execute('''
        CREATE TABLE IF NOT EXISTS images
        (id INTEGER PRIMARY KEY AUTOINCREMENT,
         filename TEXT NOT NULL,
         original_filename TEXT NOT NULL,
         upload_date DATETIME NOT NULL,
         is_active INTEGER DEFAULT 1,
         note TEXT,
         local_path TEXT NOT NULL,
         public_url TEXT NOT NULL)
    ''')
    conn.commit()
    conn.close()

def get_html_files():
    return [os.path.basename(f) for f in glob.glob(os.path.join(HTML_FOLDER, '*.html'))]

def newfilename(basename, ext):
    md5_hash = hashlib.md5()
    md5_hash.update(basename.encode('utf-8'))
    return 'qrcode-' + str(int(time.time())) + '-' + md5_hash.hexdigest() + ext

def oss_upload(filename):
    # filename not exists, just return
    if not os.path.exists(filename):
        return
    
    auth = oss2.Auth(ALI_APP_KEY, ALI_APP_SECRET)
    bucket = oss2.Bucket(auth, ALI_OSS_ENDPOINT, ALI_OSS_BUCKET)
    key = os.path.join(ALI_OSS_KEY_PREFIX.lstrip('/'), newfilename(os.path.basename(filename), os.path.splitext(filename)[-1]))
    oss2.resumable_upload(bucket, key, filename, multipart_threshold=100*1024)
    return os.path.join(IMAGE_DOMAIN, key)

def get_image_url(filename):
    """保存图片到 OSS 并获取图片的公开访问 URL"""
    return oss_upload(filename=filename)

def get_image_path(filename):
    """获取图片的本地存储路径"""
    return os.path.join(UPLOAD_FOLDER, filename)

# 登录路由
@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        password = request.form.get('password')
        if password == ADMIN_PASSWORD:
            session['logged_in'] = True
            return redirect(url_for('manage_images'))
        else:
            flash('密码错误', 'error')
    return render_template('login.html')

# 登出路由
@app.route('/logout')
def logout():
    session.pop('logged_in', None)
    return redirect(url_for('login'))

# 为现有路由添加验证
@app.route('/')
@login_required
def manage_images():
    conn = sqlite3.connect('images.db')
    c = conn.cursor()
    c.execute('SELECT * FROM images ORDER BY upload_date DESC')
    images = c.fetchall()
    conn.close()
    html_files = get_html_files()
    return render_template('manage.html', images=images, html_files=html_files)

@app.route('/upload', methods=['GET', 'POST'])
@login_required
def upload():
    if request.method == 'POST':
        if 'file' not in request.files:
            flash('没有选择文件', 'error')
            return redirect(request.url)
        
        file = request.files['file']
        if file.filename == '':
            flash('没有选择文件', 'error')
            return redirect(request.url)
        
        if file and allowed_file(file.filename):
            filename = secure_filename(file.filename)
            timestamp = datetime.now().strftime('%Y%m%d_%H%M%S_')
            saved_filename = timestamp + filename
            
            # 保存到本地
            local_path = get_image_path(saved_filename)
            file.save(local_path)
            
            # 上传到OSS并获取URL
            public_url = get_image_url(local_path)
            if not public_url:
                flash('上传到OSS失败', 'error')
                return redirect(url_for('manage_images'))
            
            # 保存到数据库
            conn = sqlite3.connect('images.db')
            c = conn.cursor()
            c.execute('''
                INSERT INTO images 
                (filename, original_filename, upload_date, local_path, public_url)
                VALUES (?, ?, ?, ?, ?)
            ''', (saved_filename, filename, datetime.now(), local_path, public_url))
            conn.commit()
            conn.close()
            
            flash('文件上传成功')
            return redirect(url_for('manage_images'))
            
    return render_template('upload.html')

@app.route('/toggle_active/<int:image_id>')
def toggle_active(image_id):
    conn = sqlite3.connect('images.db')
    c = conn.cursor()
    c.execute('UPDATE images SET is_active = 1 - is_active WHERE id = ?', (image_id,))
    conn.commit()
    conn.close()
    return redirect(url_for('manage_images'))

@app.route('/delete/<int:image_id>')
def delete_image(image_id):
    conn = sqlite3.connect('images.db')
    c = conn.cursor()
    
    # 获取文件名
    c.execute('SELECT filename FROM images WHERE id = ?', (image_id,))
    filename = c.fetchone()[0]
    
    # 删除数据库记录
    c.execute('DELETE FROM images WHERE id = ?', (image_id,))
    conn.commit()
    conn.close()
    
    # 删除文件系统中的文件
    file_path = os.path.join(UPLOAD_FOLDER, filename)
    if os.path.exists(file_path):
        os.remove(file_path)
    
    flash('图片已删除')
    return redirect(url_for('manage_images'))

@app.route('/replace', methods=['POST'])
@login_required
def replace_image():
    image_id = request.form.get('image_id')
    html_file = request.form.get('html_file')
    
    if not image_id or not html_file:
        flash('请选择图片和HTML文件', 'error')
        return redirect(url_for('manage_images'))
    
    conn = sqlite3.connect('images.db')
    c = conn.cursor()
    # 修改查询，获取public_url
    c.execute('SELECT public_url FROM images WHERE id = ? AND is_active = 1', (image_id,))
    result = c.fetchone()
    conn.close()
    
    if not result:
        flash('图片不存在或已停用', 'error')
        return redirect(url_for('manage_images'))
    
    public_url = result[0]
    html_path = os.path.join(HTML_FOLDER, html_file)
    
    try:
        with open(html_path, 'r', encoding='utf-8') as f:
            soup = BeautifulSoup(f, 'html.parser')
            img_tag = soup.find('img', id='qrcode')
            if img_tag:
                # 直接使用保存的public_url
                img_tag['src'] = public_url
                
                with open(html_path, 'w', encoding='utf-8') as f:
                    f.write(str(soup))
                flash('图片替换成功')
            else:
                flash('HTML文件中没有找到id为qrcode的img标签', 'error')
    except Exception as e:
        flash(f'替换失败: {str(e)}', 'error')
    
    return redirect(url_for('manage_images'))

@app.route('/update_note/<int:image_id>', methods=['POST'])
def update_note(image_id):
    note = request.form.get('note', '')
    
    conn = sqlite3.connect('images.db')
    c = conn.cursor()
    c.execute('UPDATE images SET note = ? WHERE id = ?', (note, image_id))
    conn.commit()
    conn.close()
    
    flash('备注已更新')
    return redirect(url_for('manage_images'))

@app.route('/favicon.ico')
def favicon():
    # 返回空响应或 404 错误
    return Response(status=204)  # 返回一个空响应 (No Content)

if __name__ == '__main__':
    debug_flag = True if os.getenv('IS_DEBUG', 'false').lower() in ('true', '1', 't', 'y', 'yes') else False
    init_db()
    app.run(host = '0.0.0.0', port = 50001, debug=debug_flag)